<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Kafka高可用性机制解析 | 技术小馆</title>
    <link href="https://cdn.staticfile.org/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
    <link href="https://cdn.staticfile.org/tailwindcss/2.2.19/tailwind.min.css" rel="stylesheet">
    <link href="https://fonts.googleapis.com/css2?family=Noto+Serif+SC:wght@400;500;600;700&family=Noto+Sans+SC:wght@300;400;500;700&display=swap" rel="stylesheet">
    <script src="https://cdn.jsdelivr.net/npm/mermaid@latest/dist/mermaid.min.js"></script>
    <style>
        body {
            font-family: 'Noto Sans SC', Tahoma, Arial, Roboto, "Droid Sans", "Helvetica Neue", "Droid Sans Fallback", "Heiti SC", "Hiragino Sans GB", Simsun, sans-serif;
            color: #333;
            line-height: 1.6;
        }
        h1, h2, h3, h4, h5, h6 {
            font-family: 'Noto Serif SC', serif;
            font-weight: 600;
            color: #222;
        }
        .hero-gradient {
            background: linear-gradient(135deg, #6e8efb 0%, #a777e3 100%);
        }
        .card-hover {
            transition: all 0.3s ease;
        }
        .card-hover:hover {
            transform: translateY(-5px);
            box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
        }
        .code-block {
            background-color: #2d2d2d;
            color: #f8f8f2;
            border-radius: 8px;
            overflow-x: auto;
        }
        .mermaid {
            background-color: white;
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
        }
        .highlight {
            position: relative;
        }
        .highlight::after {
            content: "";
            position: absolute;
            bottom: 0;
            left: 0;
            width: 100%;
            height: 4px;
            background: linear-gradient(90deg, #6e8efb, #a777e3);
            transform: scaleX(0);
            transition: transform 0.3s ease;
        }
        .highlight:hover::after {
            transform: scaleX(1);
        }
    </style>
</head>
<body class="bg-gray-50">
    <!-- Hero Section -->
    <section class="hero-gradient text-white py-20 md:py-32 px-4 md:px-0">
        <div class="container mx-auto max-w-5xl text-center">
            <h1 class="text-4xl md:text-5xl font-bold mb-6">Kafka高可用性机制解析</h1>
            <p class="text-xl md:text-2xl opacity-90 mb-8 max-w-3xl mx-auto">
                探索分布式消息系统的核心: Leader-Follower模型与副本同步机制
            </p>
            <div class="flex justify-center space-x-4">
                <a href="#overview" class="px-6 py-3 bg-white text-purple-700 rounded-full font-medium hover:bg-opacity-90 transition duration-300 flex items-center">
                    <i class="fas fa-book-open mr-2"></i> 开始阅读
                </a>
                <a href="#visualization" class="px-6 py-3 bg-transparent border-2 border-white text-white rounded-full font-medium hover:bg-white hover:bg-opacity-10 transition duration-300 flex items-center">
                    <i class="fas fa-project-diagram mr-2"></i> 可视化架构
                </a>
            </div>
        </div>
    </section>

    <!-- Main Content -->
    <main class="container mx-auto max-w-5xl px-4 md:px-0 py-12">
        <!-- Overview Section -->
        <section id="overview" class="mb-16">
            <div class="bg-white rounded-xl shadow-lg p-8 mb-8 card-hover">
                <h2 class="text-2xl font-bold mb-4 text-purple-800">Kafka的高可用性核心</h2>
                <p class="text-gray-700 mb-6">
                    Kafka 作为一个高性能的分布式消息系统，其核心特性之一就是分布式复制和高可用性。通过复制机制，Kafka 能确保消息在系统中保持冗余，避免单点故障，提高数据的可靠性和可用性。Kafka 的复制机制采用 <span class="font-bold text-purple-700">Leader-Follower</span> 模型，即每个分区有一个 <span class="font-bold text-purple-700">Leader</span> 副本，其他副本为 <span class="font-bold text-purple-700">Follower</span> 副本。Leader 负责处理所有读写请求，Follower 复制 Leader 的数据，并定期同步。
                </p>
                <div class="bg-blue-50 border-l-4 border-blue-500 p-4 rounded">
                    <div class="flex">
                        <div class="flex-shrink-0">
                            <i class="fas fa-info-circle text-blue-500 text-xl"></i>
                        </div>
                        <div class="ml-3">
                            <p class="text-sm text-blue-700">
                                <span class="font-bold">高可用性关键:</span> 通过分区副本分布在不同Broker上，即使单个节点故障，其他节点仍能提供服务。
                            </p>
                        </div>
                    </div>
                </div>
            </div>
        </section>

        <!-- Section 1 -->
        <section class="mb-16">
            <div class="flex items-center mb-6">
                <div class="bg-purple-600 text-white w-10 h-10 rounded-full flex items-center justify-center mr-4">
                    <span class="font-bold">1</span>
                </div>
                <h2 id="partition-concept" class="text-2xl font-bold text-gray-800">Kafka分区与副本的概念</h2>
            </div>
            
            <div class="grid md:grid-cols-2 gap-8">
                <div class="bg-white rounded-xl shadow-lg p-6 card-hover">
                    <h3 class="text-xl font-semibold mb-4 text-purple-700">分区与副本结构</h3>
                    <p class="text-gray-700 mb-4">
                        Kafka 中的数据存储单位是 <span class="font-bold">Topic Partition</span>，每个 Topic 可以有多个分区，每个分区的数据都被复制到多个 Broker 上，确保高可用性。每个分区有一个 <span class="font-bold">Leader</span> 副本和多个 <span class="font-bold">Follower</span> 副本。
                    </p>
                    <ul class="space-y-2">
                        <li class="flex items-start">
                            <i class="fas fa-crown text-yellow-500 mt-1 mr-2"></i>
                            <span><span class="font-bold">Leader 副本:</span> 负责处理该分区的所有读写请求，所有的写入操作都会首先写入 Leader 副本。</span>
                        </li>
                        <li class="flex items-start">
                            <i class="fas fa-users text-blue-500 mt-1 mr-2"></i>
                            <span><span class="font-bold">Follower 副本:</span> 跟随 Leader 副本，复制 Leader 上的消息。Follower 不处理请求，只有在与 Leader 保持同步时才会被认为是正常的副本。</span>
                        </li>
                    </ul>
                </div>
                
                <div class="bg-white rounded-xl shadow-lg p-6 card-hover">
                    <h3 class="text-xl font-semibold mb-4 text-purple-700">数据分布策略</h3>
                    <p class="text-gray-700">
                        在 Kafka 中，分区和副本的关系是非常关键的，它决定了消息的存储与复制规则。分区的副本通常是分布在不同的 Kafka Broker 上，确保即使某个 Broker 故障，其他 Broker 上的副本仍然能提供服务。
                    </p>
                    <div class="mt-6">
                        <div class="mermaid">
                            pie
                                title 副本分布示例 (3个Broker)
                                "Broker 1" : 35
                                "Broker 2" : 30
                                "Broker 3" : 35
                        </div>
                    </div>
                </div>
            </div>
        </section>

        <!-- Section 2 -->
        <section class="mb-16">
            <div class="flex items-center mb-6">
                <div class="bg-purple-600 text-white w-10 h-10 rounded-full flex items-center justify-center mr-4">
                    <span class="font-bold">2</span>
                </div>
                <h2 id="replication" class="text-2xl font-bold text-gray-800">Kafka的副本同步机制</h2>
            </div>
            
            <div class="bg-white rounded-xl shadow-lg p-8 mb-8 card-hover">
                <p class="text-gray-700 mb-6">
                    Kafka 的副本同步机制是实现高可用性的核心。副本同步保证了 Leader 副本和 Follower 副本的数据一致性。
                </p>
                
                <div class="grid md:grid-cols-5 gap-6 mb-8">
                    <div class="bg-purple-50 p-4 rounded-lg text-center">
                        <div class="bg-purple-100 w-12 h-12 rounded-full flex items-center justify-center mx-auto mb-3">
                            <i class="fas fa-pen-fancy text-purple-600 text-xl"></i>
                        </div>
                        <p class="font-semibold text-purple-800">写请求</p>
                        <p class="text-sm text-gray-600">客户端向Leader发送写请求</p>
                    </div>
                    <div class="flex items-center justify-center">
                        <i class="fas fa-arrow-right text-gray-400 text-xl"></i>
                    </div>
                    <div class="bg-purple-50 p-4 rounded-lg text-center">
                        <div class="bg-purple-100 w-12 h-12 rounded-full flex items-center justify-center mx-auto mb-3">
                            <i class="fas fa-file-upload text-purple-600 text-xl"></i>
                        </div>
                        <p class="font-semibold text-purple-800">日志追加</p>
                        <p class="text-sm text-gray-600">Leader将数据追加到日志文件</p>
                    </div>
                    <div class="flex items-center justify-center">
                        <i class="fas fa-arrow-right text-gray-400 text-xl"></i>
                    </div>
                    <div class="bg-purple-50 p-4 rounded-lg text-center">
                        <div class="bg-purple-100 w-12 h-12 rounded-full flex items-center justify-center mx-auto mb-3">
                            <i class="fas fa-sync-alt text-purple-600 text-xl"></i>
                        </div>
                        <p class="font-semibold text-purple-800">同步Follower</p>
                        <p class="text-sm text-gray-600">将消息同步到所有Follower</p>
                    </div>
                </div>
                
                <div class="mb-6">
                    <h3 class="text-xl font-semibold mb-3 text-purple-700">ISR (In-Sync Replicas) 集合</h3>
                    <p class="text-gray-700 mb-4">
                        Kafka 会维护一个 <span class="font-bold">ISR</span> 集合，记录所有与 Leader 副本同步的副本。只有在 ISR 集合中的副本才会被认为是健康的副本，且它们能参与到 Leader 副本的选举中。
                    </p>
                    <div class="bg-gray-100 p-4 rounded-lg">
                        <div class="mermaid">
                            graph LR
                                A[Leader] -->|同步| B[Follower1]
                                A -->|同步| C[Follower2]
                                D[Follower3] -.滞后.-> A
                                style D stroke:#ff6666
                        </div>
                    </div>
                </div>
                
                <div class="code-block p-4 rounded-lg">
                    <pre><code class="language-java">
// ReplicaFetcherThread.java
public void run() {
    while (running) {
        // 1. 从 Leader 拉取消息
        fetchMessagesFromLeader();

        // 2. 将消息追加到 Follower 的日志中
        appendMessagesToLocalLog();

        // 3. 更新 ISR 集合
        updateISR();
    }
}
                    </code></pre>
                </div>
            </div>
        </section>

        <!-- Section 3 -->
        <section class="mb-16">
            <div class="flex items-center mb-6">
                <div class="bg-purple-600 text-white w-10 h-10 rounded-full flex items-center justify-center mr-4">
                    <span class="font-bold">3</span>
                </div>
                <h2 id="leader-election" class="text-2xl font-bold text-gray-800">Leader和Follower的角色切换</h2>
            </div>
            
            <div class="grid md:grid-cols-2 gap-8">
                <div class="bg-white rounded-xl shadow-lg p-6 card-hover">
                    <h3 class="text-xl font-semibold mb-4 text-purple-700">选举流程</h3>
                    <p class="text-gray-700 mb-4">
                        在 Kafka 中，Leader 和 Follower 之间的角色切换是为了应对节点故障和提高系统的可用性。如果 Leader 副本不可用，Kafka 会自动选举一个新的 Leader 副本。
                    </p>
                    <ol class="list-decimal pl-5 space-y-2">
                        <li><span class="font-bold">Leader 副本故障:</span> 如果当前的 Leader 副本因故障或网络问题不可用</li>
                        <li><span class="font-bold">选举算法:</span> 通过 Zookeeper 中的节点状态来选举新的 Leader 副本</li>
                        <li><span class="font-bold">角色切换:</span> 新选举的副本成为 Leader，其它副本变为 Follower</li>
                    </ol>
                </div>
                
                <div class="bg-white rounded-xl shadow-lg p-6 card-hover">
                    <h3 class="text-xl font-semibold mb-4 text-purple-700">选举实现</h3>
                    <div class="code-block p-4 rounded-lg">
                        <pre><code class="language-java">
// KafkaController.java
public void onLeaderChange(TopicPartition partition, int newLeader) {
    // 1. 从 Zookeeper 获取当前副本的状态
    ReplicaState state = getReplicaState(partition);
    
    // 2. 选举新的 Leader 副本
    if (state.getLeader() != newLeader) {
        updateLeader(partition, newLeader);
    }
}
                        </code></pre>
                    </div>
                </div>
            </div>
        </section>

        <!-- Section 4 -->
        <section class="mb-16">
            <div class="flex items-center mb-6">
                <div class="bg-purple-600 text-white w-10 h-10 rounded-full flex items-center justify-center mr-4">
                    <span class="font-bold">4</span>
                </div>
                <h2 id="data-recovery" class="text-2xl font-bold text-gray-800">Kafka中的副本丢失与数据恢复</h2>
            </div>
            
            <div class="bg-white rounded-xl shadow-lg p-8 card-hover">
                <p class="text-gray-700 mb-6">
                    在 Kafka 中，当某个副本掉线或无法及时同步时，它会被标记为 <span class="font-bold">Out of Sync</span>（落后副本），并且不再参与 Leader 副本的选举。Kafka 会定期检查副本的状态，并触发数据恢复机制，确保数据的一致性。
                </p>
                
                <div class="grid md:grid-cols-3 gap-6 mb-8">
                    <div class="bg-blue-50 p-4 rounded-lg">
                        <div class="flex items-center mb-3">
                            <div class="bg-blue-100 w-8 h-8 rounded-full flex items-center justify-center mr-3">
                                <i class="fas fa-redo text-blue-600"></i>
                            </div>
                            <h4 class="font-semibold text-blue-800">恢复策略</h4>
                        </div>
                        <p class="text-sm text-gray-700">
                            当 Follower 副本恢复时，它会向 Leader 请求缺失的数据，并将其恢复到最新的状态。
                        </p>
                    </div>
                    
                    <div class="bg-purple-50 p-4 rounded-lg">
                        <div class="flex items-center mb-3">
                            <div class="bg-purple-100 w-8 h-8 rounded-full flex items-center justify-center mr-3">
                                <i class="fas fa-list-ul text-purple-600"></i>
                            </div>
                            <h4 class="font-semibold text-purple-800">ISR 集合管理</h4>
                        </div>
                        <p class="text-sm text-gray-700">
                            每个分区都有一个 ISR 集合，存储了所有同步的副本。当某个副本恢复时，Kafka 会将其重新加入 ISR 集合。
                        </p>
                    </div>
                    
                    <div class="bg-red-50 p-4 rounded-lg">
                        <div class="flex items-center mb-3">
                            <div class="bg-red-100 w-8 h-8 rounded-full flex items-center justify-center mr-3">
                                <i class="fas fa-exclamation-triangle text-red-600"></i>
                            </div>
                            <h4 class="font-semibold text-red-800">副本丢失检测</h4>
                        </div>
                        <p class="text-sm text-gray-700">
                            如果某个副本长时间无法同步，Kafka 会将其移出 ISR 集合，防止该副本影响数据一致性。
                        </p>
                    </div>
                </div>
                
                <div class="code-block p-4 rounded-lg">
                    <pre><code class="language-java">
// ReplicaFetcherThread.java
public void fetchMessagesFromLeader() {
    if (isFollowerOutOfSync()) {
        // 1. 恢复丢失的数据
        fetchMissingDataFromLeader();
    }
}
                    </code></pre>
                </div>
            </div>
        </section>

        <!-- Section 5 -->
        <section id="visualization" class="mb-16">
            <div class="flex items-center mb-6">
                <div class="bg-purple-600 text-white w-10 h-10 rounded-full flex items-center justify-center mr-4">
                    <span class="font-bold">5</span>
                </div>
                <h2 class="text-2xl font-bold text-gray-800">Kafka的高可用性架构</h2>
            </div>
            
            <div class="bg-white rounded-xl shadow-lg p-8 card-hover">
                <p class="text-gray-700 mb-6">
                    Kafka 的高可用性通过 <span class="font-bold">Leader-Follower 复制机制</span> 和 <span class="font-bold">副本同步机制</span> 来实现。在 Kafka 中，以下几个方面保证了系统的高可用性：
                </p>
                
                <div class="grid md:grid-cols-2 gap-8 mb-8">
                    <div>
                        <div class="flex items-start mb-4">
                            <div class="bg-green-100 w-8 h-8 rounded-full flex items-center justify-center mr-3 flex-shrink-0">
                                <i class="fas fa-copy text-green-600"></i>
                            </div>
                            <div>
                                <h4 class="font-semibold text-green-800">副本冗余</h4>
                                <p class="text-sm text-gray-700">
                                    每个分区有多个副本，分布在不同的 Kafka Broker 上。如果某个 Broker 故障，仍然能保证消息的可用性。
                                </p>
                            </div>
                        </div>
                        
                        <div class="flex items-start mb-4">
                            <div class="bg-purple-100 w-8 h-8 rounded-full flex items-center justify-center mr-3 flex-shrink-0">
                                <i class="fas fa-vote-yea text-purple-600"></i>
                            </div>
                            <div>
                                <h4 class="font-semibold text-purple-800">Leader 选举</h4>
                                <p class="text-sm text-gray-700">
                                    如果当前 Leader 副本不可用，Kafka 会通过 Zookeeper 选举一个新的 Leader，从而保证分区的服务可用。
                                </p>
                            </div>
                        </div>
                    </div>
                    
                    <div>
                        <div class="flex items-start mb-4">
                            <div class="bg-blue-100 w-8 h-8 rounded-full flex items-center justify-center mr-3 flex-shrink-0">
                                <i class="fas fa-sync text-blue-600"></i>
                            </div>
                            <div>
                                <h4 class="font-semibold text-blue-800">副本同步</h4>
                                <p class="text-sm text-gray-700">
                                    Kafka 会定期同步 Leader 与 Follower 之间的消息，确保数据的一致性。
                                </p>
                            </div>
                        </div>
                        
                        <div class="flex items-start">
                            <div class="bg-yellow-100 w-8 h-8 rounded-full flex items-center justify-center mr-3 flex-shrink-0">
                                <i class="fas fa-check-circle text-yellow-600"></i>
                            </div>
                            <div>
                                <h4 class="font-semibold text-yellow-800">ISR 集合</h4>
                                <p class="text-sm text-gray-700">
                                    确保只有与 Leader 副本同步的副本才能被认为是健康副本，从而参与到消息的处理和服务中。
                                </p>
                            </div>
                        </div>
                    </div>
                </div>
                
                <div class="bg-gray-100 p-6 rounded-lg">
                    <div class="mermaid">
                        graph TD
                            subgraph Kafka集群
                                B1[Broker1] -->|Leader P1| P1[分区1]
                                B1 -->|Follower P2| P2[分区2]
                                B2[Broker2] -->|Leader P2| P2
                                B2 -->|Follower P3| P3[分区3]
                                B3[Broker3] -->|Leader P3| P3
                                B3 -->|Follower P1| P1
                            end
                            Client -->|读写请求| B1
                            Client -->|读写请求| B2
                            Client -->|读写请求| B3
                    </div>
                </div>
            </div>
        </section>
    </main>

    <!-- Footer -->
    <footer class="bg-gray-900 text-gray-300 py-8">
        <div class="container mx-auto max-w-5xl px-4 md:px-0">
            <div class="flex flex-col md:flex-row justify-between items-center">
                <div class="mb-4 md:mb-0">
                    <h3 class="text-xl font-bold text-white mb-2">技术小馆</h3>
                    <p class="text-sm">探索技术世界的宝藏</p>
                </div>
                <div>
                    <a href="http://www.yuque.com/jtostring" class="text-blue-400 hover:text-blue-300 transition duration-300 flex items-center">
                        <i class="fas fa-external-link-alt mr-2"></i> www.yuque.com/jtostring
                    </a>
                </div>
            </div>
            <div class="border-t border-gray-800 mt-6 pt-6 text-center text-sm">
                <p>© 2023 技术小馆. 保留所有权利.</p>
            </div>
        </div>
    </footer>

    <script>
        mermaid.initialize({
            startOnLoad: true,
            theme: 'default',
            flowchart: {
                useMaxWidth: true,
                htmlLabels: true,
                curve: 'basis'
            }
        });
        
        // 平滑滚动
        document.querySelectorAll('a[href^="#"]').forEach(anchor => {
            anchor.addEventListener('click', function (e) {
                e.preventDefault();
                
                document.querySelector(this.getAttribute('href')).scrollIntoView({
                    behavior: 'smooth'
                });
            });
        });
        
        // 添加代码高亮
        document.addEventListener('DOMContentLoaded', function() {
            const codeBlocks = document.querySelectorAll('pre code');
            codeBlocks.forEach(block => {
                const lines = block.innerHTML.split('\n');
                const formattedLines = lines.map(line => {
                    if (line.trim().startsWith('//')) {
                        return `<span class="text-gray-500">${line}</span>`;
                    } else if (line.includes('class') || line.includes('public') || line.includes('void')) {
                        return line.replace(/(class|public|void|if|while|for|return|new|try|catch|finally)\b/g, '<span class="text-purple-400">$1</span>');
                    } else {
                        return line.replace(/"([^"]*)"/g, '<span class="text-green-400">"$1"</span>')
                                   .replace(/'([^']*)'/g, '<span class="text-green-400">\'$1\'</span>')
                                   .replace(/\b([0-9]+)\b/g, '<span class="text-yellow-400">$1</span>');
                    }
                });
                block.innerHTML = formattedLines.join('\n');
            });
        });
    </script>
</body>
</html>